#include "stdafx.h"
#include "argParser.h"
#include "exception.h"

////////////
// MayaArgParser
MayaArgParser::MayaArgParser(const std::string &cmdName)
	: cmdName(cmdName)
{
	parseNodeArgs = false;
}

MayaArgParser::~MayaArgParser()
{
	std::set<FlagParser*> deleted;
	for(FlagIt i = flags.begin();i != flags.end();++i)
	{
		FlagParser *pCur = i->second;
		if(deleted.find(pCur) == deleted.end())
		{
			delete pCur;
			deleted.insert(pCur);
		}
	}
}

void MayaArgParser::AddFlag(std::string shortName,std::string longName,FlagType flagType)
{
	if(shortName[0] == '-')
		shortName = shortName.substr(1);
	if(longName[0] == '-')
		longName = longName.substr(1);

	assert(flags.find(shortName) == flags.end());
	assert(flags.find(longName) == flags.end());	

	FlagParser *pFlagParser;
	switch(flagType)
	{
	case FLAG:
		pFlagParser = new ParamlessFlagParser(shortName,longName);
		break;

	case STRING:
		pFlagParser = new StringFlagParser(shortName,longName);
		break;

	default:
		assert(!"Invalid flag type");
	}

	flags[shortName] = pFlagParser;
	flags[longName] = pFlagParser;
}

void MayaArgParser::SetNodeArgHandling(int minNNodes,
	int maxNNodes,bool useSelectionAsDefault)
{
	assert(!parseNodeArgs);		// don't call this function multiple times.

	parseNodeArgs = true;

	this->minNNodes = minNNodes;
	this->maxNNodes = maxNNodes;
	this->useSelectionAsDefault = useSelectionAsDefault;
}

const std::vector<MDagPath> MayaArgParser::GetNodeArgs() const
{
	assert(parseNodeArgs);
	return nodesArg;
}

void MayaArgParser::Parse(const MArgList &args)
{
	MStatus res;

	int i;
	for(i = 0;i < (int)args.length();)
	{
		MString mayaArg;
		res = args.get(i,mayaArg);
		i++;
		assert(res == MStatus::kSuccess);

		std::string arg = mayaArg.asChar();

		if(arg[0] == '-')
		{
			std::string flagName = arg.substr(1,arg.length() - 1);
			FlagIt it = flags.find(flagName);
			if(it != flags.end())
			{
				it->second->Parse(args,i,this);
				it->second->SetIsSet(true);
			}
			else
			{
				throw Exception("Unknown flag specified for " + 
					cmdName + " command: -" + flagName);
			}
		}
		else
			break;
	}

	if(parseNodeArgs)
	{
		if(i < (int)args.length())
		{
			MStringArray nodes;
			unsigned int tmpIndex = (unsigned int)i;
			res = args.get(tmpIndex,nodes);
			i = (int)tmpIndex;
			if(res != MStatus::kSuccess)
			{
				throw Exception("Couldn't parse the arguments of the " + 
					cmdName + " command");
			}

			for(int j = 0;j < (int)nodes.length();j++)
			{
				MSelectionList selList;
				if(MGlobal::getSelectionListByName(nodes[i],selList) != MStatus::kSuccess)
					throw Exception(std::string(nodes[i].asChar()) + " doesn't describe node(s)");

				for(int k = 0;k < (int)selList.length();k++)
				{
					MDagPath dagPath;
					selList.getDagPath(k,dagPath);
					nodesArg.push_back(dagPath);
				}
			}

			if(i < (int)args.length())
			{
				throw Exception("Couldn't parse the arguments of the " +
					cmdName + " command");
			}
		}
		else
		{
			// use the selection instead

			MSelectionList selList;
			MGlobal::getActiveSelectionList(selList);
			int num = (int)selList.length(&res);
			assert(res == MStatus::kSuccess);
			for(int j = 0;j < num;j++)
			{
				MDagPath dagPath;
				res = selList.getDagPath(j,dagPath);
				assert(res == MStatus::kSuccess);
				nodesArg.push_back(dagPath);
			}
		}

		if((int)nodesArg.size() < minNNodes)
		{
			throw Exception("Not enough node arguments selected/specified for the " + 
				cmdName + " command.");
		}

		if((int)nodesArg.size() > maxNNodes)
		{
			throw Exception("Too many node arguments selected/specified the " + 
				cmdName + " command.");
		}
	}
	else
	{
		if(i < (int)args.length())
		{
			throw Exception("Couldn't parse the arguments of the " + 
				cmdName + " command");
		}
	}
}

bool MayaArgParser::IsFlagSet(std::string name) const
{
	if(name[0] == '-')
		name = name.substr(1);	

	ConstFlagIt it = flags.find(name);
	assert(it != flags.end());

	return it->second->IsSet();
}

const std::string& MayaArgParser::GetFlagString(std::string name) const
{
	if(name[0] == '-')
		name = name.substr(1);
	
	ConstFlagIt it = flags.find(name);
	assert(it != flags.end());
	assert(it->second->GetFlagType() == STRING);

	if(it->second->IsSet())
		return ((StringFlagParser*)it->second)->GetValue();
	else
	{
		throw Exception("The " + it->second->GetShortName() + " / " + 
			it->second->GetLongName() + " must be specified to the " + cmdName + " command");
	}

}

//////////
// MayaArgParser::StringFlagParser
MayaArgParser::StringFlagParser::StringFlagParser(
	const std::string &shortName,const std::string &longName)
	: FlagParser(shortName,longName)
{
	specified = false;
}

void MayaArgParser::StringFlagParser::Parse(const MArgList &argList,
	int &index,MayaArgParser *pArgParser)
{
	MStatus res;

	specified = true;

	if(index < (int)argList.length())
	{
		MString mayaArg;
		res = argList.get(index,mayaArg);
		if(res != MStatus::kSuccess)
		{
			throw Exception("The value specified after the " + GetShortName() + " / " + 
				GetLongName() + " flag of the " + pArgParser->GetCommandName() + 
				" command isn't a string (or isn't convertable to it).");
		}

		value = mayaArg.asChar();
	}
	else
	{
		throw Exception("The " + GetShortName() + " / " + 
			GetLongName() + " flag of the " + pArgParser->GetCommandName() + 
			" command should be followed by a string value");
	}
}